package com.my.chapter17;

import java.util.*;

import static java.util.Collections.binarySearch;

public class practice10 {
    public static void main(String[] args) {
        SortedSet<String> sortedSet = new MySortedSet<String>();
        Collections.addAll(sortedSet,
                "one two three four five six seven eight"
                        .split(" "));
        System.out.println(sortedSet);
        String low = sortedSet.first();
        String high = sortedSet.last();
        System.out.println("low = " + low);
        System.out.println("high = " + high);
        Iterator<String> it = sortedSet.iterator();
        for(int i = 0; i <= 6; i++) {
            if(i == 3) low = it.next();
            if(i == 6) high = it.next();
            else it.next();
        }
        System.out.println(low);
        System.out.println(high);
        System.out.println(sortedSet.subSet(low,high));
        System.out.println(sortedSet.headSet(high));
        System.out.println(sortedSet.tailSet(low));
        System.out.println(sortedSet.contains("three"));
        System.out.println(sortedSet.addAll(Arrays.asList("three","eleven")));
        System.out.println(sortedSet);
        System.out.println(sortedSet.retainAll(Arrays.asList("three","eleven")));
        System.out.println(sortedSet);
        try {
            sortedSet.addAll(Arrays.asList("zero", null));
        } catch(NullPointerException e) {
            System.out.println("Null elements not supported!");
        }
        System.out.println(sortedSet);
    }
}

class MySortedSet<T> implements SortedSet<T>{
    private final List<T> list;
    public MySortedSet(){
        list = new LinkedList<T>();
    }

    private MySortedSet(List<T> list){
        this.list = list;
    }

    @Override
    public String toString() {
        return list.toString();
    }

    @Override
    public Comparator<? super T> comparator() {
        return null;
    }

    @Override
    public SortedSet<T> subSet(T fromElement, T toElement) {
        checkForNull(fromElement);
        checkForNull(toElement);
        int fromIndex = list.indexOf(fromElement);
        int toIndex = list.indexOf(toElement);
        checkForValidIndex(fromIndex);
        checkForValidIndex(toIndex);
        try {
            return new MySortedSet<T>(list.subList(fromIndex,toIndex));
        }catch (IndexOutOfBoundsException e){
            throw new IllegalArgumentException(e);
        }
    }

    @Override
    public SortedSet<T> headSet(T toElement) {
        checkForNull(toElement);
        int toIndex = list.indexOf(toElement);
        checkForValidIndex(toIndex);
        try {
            return new MySortedSet<T>(list.subList(0,toIndex));
        }catch (IndexOutOfBoundsException e){
            throw new IllegalArgumentException(e);
        }
    }

    @Override
    public SortedSet<T> tailSet(T fromElement) {
        checkForNull(fromElement);
        int fromIndex = list.indexOf(fromElement);
        checkForValidIndex(fromIndex);
        try {
            return new MySortedSet<T>(list.subList(fromIndex,list.size()));
        }catch (IndexOutOfBoundsException e){
            throw new IllegalArgumentException(e);
        }
    }

    @Override
    public T first() {
        try {
            return list.get(0);
        } catch(IndexOutOfBoundsException e) {
            throw new NoSuchElementException();
        }
    }

    @Override
    public T last() {
        try {
            return list.get(list.size() - 1);
        } catch(IndexOutOfBoundsException e) {
            throw new NoSuchElementException();
        }
    }

    @Override
    public int size() {
        return list.size();
    }

    @Override
    public boolean isEmpty() {
        return list.isEmpty();
    }

    @SuppressWarnings("unchecked")
    public boolean contains(Object o) {
        checkForNull(o);
        return binarySearch((List<Comparable<T>>)list, (T)o) >= 0;
    }

    @Override
    public Iterator<T> iterator() {
        return list.iterator();
    }

    @Override
    public Object[] toArray() {
        return list.toArray();
    }

    @Override
    public <T1> T1[] toArray(T1[] a) {
        return list.toArray(a);
    }

    @SuppressWarnings("unchecked")
    public boolean add(T t) {
        checkForNull(t);
        int ip = binarySearch((List<Comparable<T>>)list,t);
        if(ip<0){
            ip = -(ip+1);
            if(ip == list.size()){
                list.add(t);
            }else{
                list.add(ip,t);
            }
            return true;
        }
        return false;
    }

    @Override
    public boolean remove(Object o) {
        checkForNull(0);
        return list.remove(o);
    }

    @Override
    public boolean containsAll(Collection<?> c) {
        checkForNull(c);
        return list.containsAll(c);
    }

    @Override
    public boolean addAll(Collection<? extends T> c) {
        checkForNull(c);
        checkForNullElements(c);
        boolean res = false;
        for(T item:c){
            res |= add(item);
        }
        return res;
    }

    @Override
    public boolean retainAll(Collection<?> c) {
        checkForNull(c);
        return list.retainAll(c);
    }

    @Override
    public boolean removeAll(Collection<?> c) {
        checkForNull(c);
        return list.removeAll(c);
    }

    @Override
    public void clear() {
        list.clear();
    }

    public boolean equals(Object o) {
        return o instanceof MySortedSet &&
                list.equals(((MySortedSet<?>)o).list);
    }
    public int hashCode() { return list.hashCode(); }




    private void checkForNullElements(Collection<?> c) {
        for(Iterator<?> it = c.iterator(); it.hasNext();)
            if(it.next() == null)
                throw new NullPointerException();
    }
    private void checkForNull(Object o) {
        if(o == null)
            throw new NullPointerException();
    }
    private void checkForValidIndex(int idx) {
        if(idx == -1)
            throw new IllegalArgumentException();
    }
}
